package com.hd123.rumba.gwt.widget2.client.hotkey;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
import com.google.gwt.user.client.ui.FocusWidget;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;
import com.hd123.rumba.gwt.widget2.client.action.RActionBase;

/**
 * 热键管理器
 * 
 * @author LiQi
 * 
 */
public class RHotKeyManager {

  /** 热键对应的动作 */
  private static List<RHotKeyRegistration> registry = new ArrayList();

  /**
   * 禁止实例化
   * 
   */
  private RHotKeyManager() {
  }

  static {
    init();
  }

  /**
   * 添加一个热键
   * 
   * @param hotKey
   *          热键
   * @param command
   *          按下热键时执行的动作
   */
  public static void add(RHotKey hotKey, Command command) {
    add(hotKey, null, command);
  }

  /**
   * 添加一个热键
   * 
   * @param hotKey
   *          热键
   * @param owner
   *          热键所属的对象或控件，允许null
   * @param command
   *          按下热键时执行的动作
   */
  public static void add(RHotKey hotKey, Object owner, Command command) {
    if (hotKey == null)
      throw new NullPointerException("hotKey不允许空值");
    if (command == null)
      throw new NullPointerException("command不允许空值");

    RHotKeyRegistration reg = new RHotKeyRegistration();
    reg.setHotKey(hotKey);
    reg.setCommand(command);
    reg.setOwner(owner);
    registry.add(reg);
  }

  /**
   * 删除热键
   * 
   * @param owner
   *          热键所属的对象或控件
   */
  public static void remove(Object owner) {
    if (owner == null)
      return;

    for (Iterator<RHotKeyRegistration> i = registry.iterator(); i.hasNext();) {
      if (owner.equals(i.next().getOwner()))
        i.remove();
    }
  }

  /**
   * 初始化
   * 
   */
  private static void init() {
    // 监视全局键盘消息
    Event.addNativePreviewHandler(new NativePreviewHandler() {
      public void onPreviewNativeEvent(NativePreviewEvent event) {
        if (event.getTypeInt() != Event.ONKEYDOWN)
          return;

        //********************************************************************************
        // 调试代码
        //        String s = "ctrl=" + event.getNativeEvent().getCtrlKey() + ", alt="
        //            + event.getNativeEvent().getAltKey() + ", shift="
        //            + event.getNativeEvent().getShiftKey() + ", keyCode="
        //            + event.getNativeEvent().getKeyCode();
        //        GWT.log(s, null);
        //        Window.alert(s);
        //********************************************************************************

        boolean hotKeyFired = false;
        for (RHotKeyRegistration reg : registry) {
          // 匹配热键
          if (reg.getHotKey() == null || !reg.getHotKey().isEnabled()
              || !reg.getHotKey().isMatching(event.getNativeEvent()))
            continue;

          // 检查widget状态
          if (reg.getOwner() instanceof Widget && !(reg.getOwner() instanceof RActionBase)) {
            Widget widget = (Widget) reg.getOwner();
            if (!widget.isVisible() || !widget.isAttached() || !isVisibleInDOMTree(widget))
              continue;
          }

          // 检查FocusWidget状态
          if (reg.getOwner() instanceof FocusWidget) {
            FocusWidget widget = (FocusWidget) reg.getOwner();
            if (!widget.isEnabled())
              continue;
          }

          // 执行动作
          if (reg.getCommand() != null) {
            hotKeyFired = true;
            reg.getCommand().execute();
          }
        }

        // 取消事件
        if (hotKeyFired)
          event.cancel();
      }
    });
  }

  /**
   * 检查控件及其在DOM树上的所有父节点的可见性，任何一个父节点不可见就会返回false
   * 
   * @param widget
   * @return
   */
  private static boolean isVisibleInDOMTree(Widget widget) {
    Element element = widget.getElement();
    while (element != null) {
      if (!UIObject.isVisible(element))
        return false;
      element = element.getParentElement();
    }
    return true;
  }

  /**
   * 热键注册项
   * 
   * @author LiQi
   * 
   */
  public static class RHotKeyRegistration {

    /** 热键 */
    private RHotKey hotKey;

    /** 热键所属的对象或控件 */
    private Object owner;

    /** 动作 */
    private Command command;

    RHotKey getHotKey() {
      return hotKey;
    }

    void setHotKey(RHotKey hotKey) {
      this.hotKey = hotKey;
    }

    Object getOwner() {
      return owner;
    }

    void setOwner(Object owner) {
      this.owner = owner;
    }

    Command getCommand() {
      return command;
    }

    void setCommand(Command command) {
      this.command = command;
    }

    public void remove() {
      registry.remove(this);
    }
  }
}
